﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Media;
using LibEBML.Common;
using VBFOOL.MediaPlayer.Core;
using VBFOOL.MediaPlayer.Core.Interface;
namespace VBFOOL.MediaPlayer.MKV
{

    class MKVMediaStream : IMediaStream
    {
        internal MKVMediaStream(MKVFileSource source, LibEBML.Common.TrackEntry track, MediaStreamType MediaType, ICodecFilter Codec)
        {
            m_source = source;
            m_Codec = Codec;
            Dictionary<string, object> obj = new Dictionary<string, object>();
            obj["CodecPrivateData"] = track.CodecPrivate;
            switch (track.TrackType)
            {
                case 1:
                    if (track.Video != null)
                    {
                        obj["Width"] = track.Video.PixelWidth;
                        obj["Height"] = track.Video.PixelHeight;
                    }
                    break;
                case 2:
                    if (track.Audio != null)
                    {
                        obj["Channels"] = track.Audio.Channels;
                        obj["SamplingFrequency"] = track.Audio.SamplingFrequency;
                        obj["BitDepth"] = track.Audio.BitDepth;
                    }
                    break;
                default:
                    break;
            }
            var att = m_Codec.GetCodecPrivateData(obj);
            desc = new MediaStreamDescription(MediaType, att);
            this.Track = track;
            if (track.ContentEncodings != null && track.ContentEncodings.Count > 0)
            {
                var ce = track.ContentEncodings[0];
                if (ce.ContentEncodingType == 0)
                {
                    if (ce.Compression.ContentCompAlgo == 3)
                    {
                        CompressHeader = ce.Compression.ContentCompSettings;
                    }
                }
            }
            TimeCodeScale = m_source.Document.Segment.Info.TimecodeScale / 100;
            DefaultDuration = track.DefaultDuration / 100;
        }
        long DefaultDuration = 0;
        MKVFileSource m_source;
        TrackEntry Track = null;
        public IInputSource Source { get { return m_source; } }
        MediaStreamDescription desc;
        long TimeCodeScale = 10000;
        byte[] CompressHeader = null;
        internal void Seek(int ClusterIndex, int BlockIndex)
        {
            FrameQueue.Clear();

            CurrentStep.ClusterIndex = ClusterIndex;
            CurrentStep.BlockIndex = BlockIndex;
            FillVideoQueue();
        }

        public object CodecVariable { get; set; }

        class BlockStep
        {
            internal int ClusterIndex = 0;
            internal int BlockIndex = 0;
        }
        private BlockStep GetNextBlockStep(BlockStep sp)
        {
            var Clusters = m_source.Document.Segment.Clusters;
            var cb = new BlockStep() { ClusterIndex = sp.ClusterIndex, BlockIndex = sp.BlockIndex + 1 };
            while (true)
            {
                if (Clusters.Count > cb.ClusterIndex)
                {
                    var c = Clusters[cb.ClusterIndex];
                    if (c.Blocks.Count > cb.BlockIndex)
                    {
                        var b = c.Blocks[cb.BlockIndex];
                        if (b.TrackNumber == Track.TrackNumber)
                        {
                            return cb;
                        }
                        cb.BlockIndex++;
                    }
                    else
                    {
                        cb.ClusterIndex++;
                        cb.BlockIndex = 0;
                    }
                }
                else
                {
                    return null;
                }
            }
        }
        BlockStep CurrentStep = new BlockStep();

        Queue<FrameData> FrameQueue = new Queue<FrameData>();
        private void FillVideoQueue()
        {
            if (state < 0)
            {
                return;
            }
            var Clusters = m_source.Document.Segment.Clusters;
            var nb = GetNextBlockStep(CurrentStep);
            if (nb != null)
            {
                Cluster curcluster = Clusters[nb.ClusterIndex];
                SimpleBlock curblock = curcluster.Blocks[nb.BlockIndex];
                long lp = curcluster.TimeCode + curblock.TimeCode;
                System.IO.MemoryStream ms = new System.IO.MemoryStream();
                long p = 0, mt = 0, pc = DefaultDuration;
                var dts = curblock.GetBlockData(CompressHeader);
                if (curblock is Block)
                {
                    var bs = curblock as Block;
                    if (bs.BlockDuration > 0)
                    {
                        pc = bs.BlockDuration * TimeCodeScale;
                    }
                }
                foreach (var item1 in dts)
                {
                    FrameData fd = new FrameData();
                    fd.TimeCode = lp * TimeCodeScale + mt;
                    var b = Codec.Translate(item1, 0, item1.Length);
                    ms.Write(b, 0, b.Length);
                    fd.Data = ms;
                    fd.Position = p;
                    p += b.Length;
                    fd.Duration = pc;
                    fd.Length = b.Length;
                    FrameQueue.Enqueue(fd);
                    mt += pc;
                }

                CurrentStep = nb;
            }
            else
            {
                state = -1;
                FrameData fd = new FrameData();
                FrameQueue.Enqueue(fd);
                return;
            }
        }

        public MediaStreamDescription Description
        {
            get { return desc; }
        }
        public double BufferProgress
        {
            get
            {
                return 1;
            }
        }
        int state = 0;
        public FrameData GetFrame()
        {
            while (FrameQueue.Count <= 0 && state >= 0)
            {
                FillVideoQueue();
            }
            try
            {
                return FrameQueue.Dequeue();
            }
            catch (Exception)
            {

                throw;
            }
        }
        ICodecFilter m_Codec;
        public ICodecFilter Codec
        {
            get { return m_Codec; }
        }
    }
}
//SimpleBlock curblock = null;
//while (true)
//{
//    if (Clusters.Count > ClutserIndex)
//    {
//        var c = Clusters[ClutserIndex];
//        if (c.Blocks.Count > BlockIndex)
//        {
//            var b = c.Blocks[BlockIndex];
//            BlockIndex++;
//            if (b.TrackNumber == Track.TrackNumber)
//            {
//                curblock = b;
//                lp = c.TimeCode + curblock.TimeCode;
//                break;
//            }
//        }
//        else
//        {
//            ClutserIndex++;
//            BlockIndex = 0;
//        }
//    }
//    else
//    {
//        state = -1;
//        FrameData fd = new FrameData();
//        FrameQueue.Enqueue(fd);
//        return;

//    }
//}